Skip to content

Claude/implement e2e plan yt1o c - #68

Merged
Anthony-Bible merged 1 commit into
mainfrom
claude/implement-e2e-plan-yt1oC
May 16, 2026
Merged

Claude/implement e2e plan yt1o c#68
Anthony-Bible merged 1 commit into
mainfrom
claude/implement-e2e-plan-yt1oC

Conversation

@Anthony-Bible

Copy link
Copy Markdown
Owner

No description provided.

@claude

claude Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Anthony-Bible's task in 2m 41s —— View job


PR Review: E2E Load-Investigation Test Harness

  • Gather context and understand the PR
  • Read and analyze changed files
  • Review shell scripts for security and correctness
  • Review Docker Compose configuration
  • Review Prometheus/Alertmanager configuration
  • Review documentation
  • Post comprehensive review feedback

Overall this is a well-structured E2E harness with clear separation of concerns and good documentation intent. A few reliability bugs and one likely functional defect need attention before this can be trusted to pass/fail correctly in CI.


Critical Issues

Port mapping mismatch will break readiness check

docker-compose.yml maps 8091:8081 with the comment # internal /health, /ready, but agent-config/alert-sources.yaml only configures addr: ":8080" — a single bind address. If the agent serves all endpoints (webhook, /health, /ready) on port 8080, then run-test.sh's readiness probe at http://localhost:8091/ready will always fail, timing out and failing the test.

# deploy/e2e/docker-compose.yml:70-71
ports:
  - "8090:8080"   # public webhook
  - "8091:8081"   # internal /health, /ready  ← broken if agent doesn't bind :8081

The readiness loop in run-test.sh:41 hits localhost:8091/ready. If the agent truly only exposes one port, this mapping should be 8091:8080 and the webhook port should be removed or aliased. Fix this →


Test pass condition validates existence, not correctness

run-test.sh:61-69 passes the test as soon as any .json file appears in the investigations directory. An empty investigation, an errored investigation, or even a file written by something else would trigger a Test PASSED. The test should validate that the JSON contains a non-empty RCA finding with a populated root cause. Fix this →


Shell Script Issues

Unquoted find output causes word splitting on filenames with spaces (run-test.sh:64)

# run-test.sh:64 — unsafe
for f in $(find agent-investigations -type f -name '*.json'); do

Use find ... -print0 | while IFS= read -r -d '' f or mapfile -d '' -t files < <(find ...) instead. Fix this →

generate-load.sh uses set -u but not set -e or set -o pipefail (load-gen/generate-load.sh:2)

Inconsistent error handling versus every other script in the harness. If stress-ng is missing or fails, the script continues silently. Fix this →

500 background curl processes launched simultaneously (load-gen/generate-load.sh:14-16)

for i in $(seq 1 500); do
  curl -s -m 2 http://target:8080/ >/dev/null 2>&1 &
done

Spawning 500 goroutines in bash will exhaust the process table on constrained CI runners (Docker-in-Docker environments often have tight PID limits). Consider batching with xargs -P 20 or limiting concurrency to avoid fork: resource temporarily unavailable. Fix this →


Security / Permissions

agent-investigations directory is world-writable (run-test.sh:25)

chmod 0777 agent-investigations || true

0777 is overly permissive. The comment says "Ensure the non-root container user (UID 10001) can write here." — this is better accomplished by chmod 0755 + chown $(id -u):$(id -g), or using Docker's --user flag. Even in a test environment, 0777 is a bad habit that's easy to accidentally replicate. Fix this →


Docker Compose Issues

depends_on without health checks creates race conditions (docker-compose.yml:24-75)

All depends_on entries start the next container as soon as the dependency starts, not when it's ready. Prometheus may try to contact Alertmanager before it has finished initializing. The run-test.sh does have a wait loop for the agent's /ready endpoint, but Alertmanager and Prometheus have no such protection. Adding healthcheck blocks and using depends_on: condition: service_healthy would make the boot sequence reliable.

Duplicate --auto-approve-safe configuration (docker-compose.yml:51,59)

AGENT_SAFETY_AUTO_APPROVE_SAFE=true (env var, line 51) and --auto-approve-safe (CLI flag, line 59) both set the same option. One should be removed to avoid confusion about which mechanism is authoritative.


Architecture / Design Issues

setup.sh heredoc approach creates a maintenance trap

setup.sh regenerates all the committed files from heredocs. This means every change requires updating both the actual file and the heredoc in setup.sh, with no automated check that they stay in sync. The stated goal "running it on a clean checkout should produce git diff --stat deploy/e2e/ == empty" won't be verified unless there's a CI job that runs setup.sh && git diff --exit-code. Consider either:

  • Removing setup.sh and keeping only the committed files (simpler), or
  • Adding a CI step that validates setup.sh produces no diff

Alertmanager config routes are redundant (alertmanager/alertmanager.yml:9-15)

Both severity: critical and severity: warning sub-routes point to the same code-agent receiver, which is already the default root receiver. The sub-routes can be removed without changing behavior.


Documentation Inconsistency

Docs reference hey/wrk2 but implementation uses stress-ng/curl (docs/e2e-load-investigation-test-plan.md:19,43)

The component inventory table says the load generator uses rakyll/hey or williamyao/wrk2, but load-gen/Dockerfile installs stress-ng and curl. The docs should be updated to reflect the actual implementation.


Minor Notes

  • target/start.sh uses set -e but runs node_exporter in the background (&). If node_exporter fails to start, set -e won't catch it since background processes don't trigger exit-on-error. A wait with exit code check or a readiness probe would improve reliability.
  • teardown.sh sets set -euo pipefail but its only real command uses || true, making the strict mode largely ineffective — fine for a teardown script, just slightly inconsistent.
  • The skill at agent-config/skills/e2e-target/SKILL.md hints at the expected root cause (stress-ng workload in the e2e-load-gen container). This is good for guiding the AI but could lead the investigation too strongly — worth noting in test result interpretation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an end-to-end load-investigation test harness under deploy/e2e/ along with a long-form planning document in docs/. The harness brings up a Docker Compose stack (target VM with node_exporter, Prometheus, Alertmanager, the code-agent itself, and a stress-ng based load generator), wires Alertmanager webhooks to the agent's /alerts/prometheus endpoint, and provides scripts to scaffold, run, and tear down the test.

Changes:

  • New docs/e2e-load-investigation-test-plan.md describing architecture, phases, success criteria, and risks.
  • New deploy/e2e/ tree: target/load-gen Dockerfiles + scripts, Prometheus/Alertmanager configs, agent alert-source config and e2e-target skill, and a working docker-compose.yml.
  • setup.sh (regenerator), run-test.sh (build/start/poll for investigation JSON/teardown), and teardown.sh.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
docs/e2e-load-investigation-test-plan.md High-level plan; some snippets have drifted from the committed implementation.
deploy/e2e/docker-compose.yml Real stack definition; uses explicit command and per-file mounts for config/skills.
deploy/e2e/setup.sh Regenerates most of the tree from heredocs (but not compose/run/teardown).
deploy/e2e/run-test.sh Builds, starts, waits on /ready, polls agent-investigations/ for JSON, dumps debug on timeout.
deploy/e2e/teardown.sh docker compose down -v + clears agent-investigations/.
deploy/e2e/target/{Dockerfile,start.sh,server.sh} Alpine + prometheus-node-exporter + naive nc HTTP server.
deploy/e2e/load-gen/{Dockerfile,generate-load.sh} stress-ng CPU/mem stress + curl flood after 30 s baseline.
deploy/e2e/prometheus/{prometheus.yml,alert_rules.yml} 5 s scrape, CPU>80%/mem>85%/load>2 alerts.
deploy/e2e/alertmanager/alertmanager.yml Routes both severities to code-agent webhook.
deploy/e2e/agent-config/alert-sources.yaml Single Prometheus webhook source on /alerts/prometheus.
deploy/e2e/agent-config/skills/e2e-target/SKILL.md Skill with investigation cheatsheet for the agent.
deploy/e2e/.gitignore, .env.template Ignore .env/investigations; API-key template.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +143 to +170
COPY <<'EOF' /app/server.sh
#!/bin/bash
# Demo HTTP service — intentionally inefficient for load testing
# Simulates a "production service" with CPU-spikey endpoints
while true; do
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nok $(date)" \
| nc -l -p 8080 -q 0
done &
EOF

# Startup: node_exporter + demo service
COPY <<'EOF' /app/start.sh
#!/bin/bash
/usr/bin/node_exporter \
--web.listen-address=0.0.0.0:9100 \
--collector.cpu \
--collector.meminfo \
--collector.loadavg \
--collector.diskstats \
--collector.filesystem \
--collector.netdev &
/app/server.sh
EOF

RUN chmod +x /app/start.sh /app/server.sh
EXPOSE 9100 8080
CMD ["/app/start.sh"]
```
Comment on lines +364 to +368
allowed-tools:
- bash
- read_file
- list_files
- fetch
Comment on lines +52 to +131
```yaml
version: "3.9"

services:
target:
build:
context: ./target
dockerfile: Dockerfile
container_name: e2e-target
ports:
- "9100:9100" # node_exporter
- "8080:8080" # demo HTTP service
networks:
- e2e-net
labels:
env: "e2e-test"

prometheus:
image: prom/prometheus:v3.3.1
container_name: e2e-prometheus
volumes:
- ./prometheus:/etc/prometheus
ports:
- "9090:9090"
networks:
- e2e-net
depends_on:
- target

alertmanager:
image: prom/alertmanager:v0.28.1
container_name: e2e-alertmanager
volumes:
- ./alertmanager:/etc/alertmanager
ports:
- "9093:9093"
networks:
- e2e-net
depends_on:
- prometheus

code-agent:
build:
context: ../..
dockerfile: Dockerfile
container_name: e2e-code-agent
env_file:
- .env
environment:
- AGENT_SERVE_ADDR=:8080
- AGENT_AI_PROVIDER=anthropic
- AGENT_SAFETY_AUTO_APPROVE_SAFE=true
- AGENT_SAFETY_COMMAND_VALIDATION_MODE=blacklist
- AGENT_LOG_LEVEL=debug
- AGENT_LOG_FORMAT=json
volumes:
- ./agent-config:/app/config
- ./agent-investigations:/app/.agent
ports:
- "8090:8080" # webhook (avoid clash with target :8080)
- "8091:8081" # health/ready
networks:
- e2e-net
depends_on:
- alertmanager

load-generator:
build:
context: ./load-gen
dockerfile: Dockerfile
container_name: e2e-load-gen
networks:
- e2e-net
depends_on:
- target

networks:
e2e-net:
driver: bridge
```
Comment on lines +478 to +492
```bash
# Check investigation output files
ls -la deploy/e2e/agent-investigations/

# Read the investigation result
cat deploy/e2e/agent-investigations/*.json | jq .

# Expected fields in the result:
# - investigation_id
# - status: "completed" or "escalated"
# - confidence: 0.0-1.0
# - findings: array of RCA findings
# - actions_taken: number of tool calls
# - duration: time spent investigating
```
### 8.1 Real VM (instead of Docker container)

For a more realistic test, use a real VM:
- Spin up a GCP/GCP/AWS instance
Comment thread deploy/e2e/setup.sh Outdated
Comment on lines +2 to +6
# Regenerates the entire deploy/e2e/ tree from heredocs.
# The repo already ships these files committed; this script is a single
# source-of-truth refresher and a drift detector — running it on a clean
# checkout should produce `git diff --stat deploy/e2e/` == empty (apart
# from .env, which is gitignored).
Comment thread deploy/e2e/run-test.sh Outdated
Comment on lines +64 to +68
for f in $(find agent-investigations -type f -name '*.json'); do
echo "--- $f ---"
python3 -m json.tool < "$f" 2>/dev/null || cat "$f"
echo
done
@Anthony-Bible

Copy link
Copy Markdown
Owner Author

@copilot apply changes based on the comments in this thread

Copilot AI commented May 16, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Applied the review-thread fixes in commit b3e9c26. This updates the E2E plan snippets to match the committed compose/target implementations, corrects allowed-tools formatting and investigation file paths, fixes the GCP/AWS typo, weakens the setup.sh drift-detector claim, and hardens run-test.sh JSON file iteration against whitespace/special chars.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.anthropic.com
    • Triggering command: /tmp/go-build2057058963/b368/integration.test /tmp/go-build2057058963/b368/integration.test -test.testlogfile=/tmp/go-build2057058963/b368/testlog.txt -test.paniconexit0 -test.timeout=10m0s -goversion go1.24.13 -c=4 -nolocalimports -importcfg /tmp/go-build2057058963/b314/importcfg -pack OZAD3_TYqT_- (dns block)
  • example.com
    • Triggering command: /usr/bin/curl curl REDACTED 7LNR3Fr4CsSzz7DWdxS0/7LNR3Fr4CsSzz7DWdxS0 -goversion go1.24.13 -c=4 -nolocalimports -importcfg /tmp/go-build2057058963/b332/importcfg -pack /home/REDACTED/work/code-agent-demo/code-agent-demo/internal/infrastructure/adapter/ai/anthropic_adapter.go /home/REDACTED/work/code-agent-demo/code-agent-demo/internal/infrastructure/adapter/ai/anthropic_adapter_test.go (dns block)
  • httpbin.org
    • Triggering command: /tmp/go-build2057058963/b352/tool.test /tmp/go-build2057058963/b352/tool.test -test.testlogfile=/tmp/go-build2057058963/b352/testlog.txt -test.paniconexit0 -test.timeout=10m0s /tmp/go-build2057058963/b266/vet.cfg (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@Anthony-Bible

Copy link
Copy Markdown
Owner Author

@claude please review the PR now.

@claude

claude Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Anthony-Bible's task in 2m 44s —— View job


PR Review: E2E Load-Investigation Test Harness

  • Gather context and understand the PR
  • Read changed files
  • Analyze for bugs, security issues, and correctness
  • Post comprehensive review feedback

This PR adds a well-structured E2E harness that exercises the full alert-investigation pipeline under synthetic load. The architecture is sound — the two-port design, recursive find loop, and per-service volume mounts are all handled correctly. A few real issues remain that should be addressed before this can reliably signal pass/fail in CI.


Correction to the previous Claude review

The previous review flagged 8091:8081 as a broken port mapping. This is not a bug. The webhook adapter intentionally binds two ports: :8080 for webhooks and :8081 for /health+/ready (see http_adapter.go:35-52,116-117). The docker-compose mapping is correct.


Critical: Test pass condition doesn't validate investigation content

run-test.sh:61-69 passes the test the moment any .json file appears in agent-investigations/. An errored investigation, a zero-byte write, or a race where the file is created before content is flushed would all trigger "Test PASSED". The test should verify that the file contains a non-empty RCA result:

# after confirming FILES > 0, add before declaring PASSED:
if ! python3 -c "
import json, sys, glob
files = glob.glob('agent-investigations/**/*.json', recursive=True)
data = json.load(open(files[0]))
rca = data.get('rca_findings') or data.get('findings') or data.get('rca')
if not rca:
    sys.exit(1)
" 2>/dev/null; then
  echo "ERROR: investigation file exists but RCA findings are empty" >&2
  exit 1
fi

Fix this →


Shell Script Issues

generate-load.sh uses set -u but not set -e or set -o pipefail (load-gen/generate-load.sh:2)

All other scripts in the harness use set -euo pipefail. If stress-ng is missing or the binary fails, the script silently continues. Fix this →

500 background curl processes at once (load-gen/generate-load.sh:14-16)

Spawning 500 shell background processes simultaneously risks exhausting the PID table on constrained CI runners. Consider batching with xargs -P 20 or a semaphore loop: Fix this →

node_exporter exit is silently ignored (target/start.sh:4-11)

start.sh uses set -e, but backgrounded processes (&) bypass it. If node_exporter fails to bind :9100, start.sh continues, exec /app/server.sh runs, Prometheus gets no metrics, and no alerts fire — test fails without an obvious reason. Fix this →


Security / Permissions

chmod 0777 on agent-investigations/ (run-test.sh:25)

The 0777 is functionally required here — the bind mount replaces the Dockerfile's pre-created /app/.agent directory, so the container's UID 10001 agent user needs write permission to create the investigations/ subdirectory. However, 0777 is overly broad. A more targeted fix is to pre-create the subdirectory on the host with appropriate permissions before starting the stack:

mkdir -p agent-investigations/investigations
chmod 0755 agent-investigations agent-investigations/investigations

Fix this →


Docker Compose Issues

Duplicate --auto-approve-safe configuration (docker-compose.yml:51,59)

AGENT_SAFETY_AUTO_APPROVE_SAFE=true (line 51) and --auto-approve-safe (line 59) both set the same option. One should be removed. Fix this →

depends_on without health checks creates startup race conditions (docker-compose.yml:24-75)

depends_on only waits for a container to start, not to be ready. Prometheus may begin scraping before Alertmanager is up; Alertmanager may try to contact a not-yet-initialized code-agent. The run-test.sh wait loop covers the agent, but the other services have no protection. Consider adding healthcheck blocks and condition: service_healthy.


Design Issues

Alertmanager sub-routes are redundant (alertmanager/alertmanager.yml:9-15)

Both severity: critical and severity: warning sub-routes route to code-agent, which is already the root receiver. These two sub-routes can be removed entirely without changing behavior. Fix this →

setup.sh scope doesn't cover all committed files (setup.sh:1-4)

docker-compose.yml, run-test.sh, teardown.sh, and .env.template are all committed under deploy/e2e/ but are not regenerated by setup.sh. Drift in those files won't be caught. The comment was already weakened by a previous commit, which is the right call — but worth noting that docker-compose.yml in particular is the most likely to drift.


Minor Notes

  • The e2e-target skill (SKILL.md:41) explicitly tells the agent the expected root cause (stress-ng workload in the e2e-load-gen container). This is helpful for guiding investigation but means a successful test doesn't fully validate unsupervised root-cause discovery — worth noting in test result interpretation.
  • teardown.sh uses set -euo pipefail but all real commands use || true, making the strict mode largely decorative. Fine for a teardown script.
  • repeat_interval: 1m in Alertmanager means resolved alerts can re-fire within the test window. For a 300s test this is acceptable but could create duplicate investigations.

@claude

claude Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Anthony-Bible's task in 2m 12s —— View job


PR Review: E2E Load-Investigation Test Harness (Post-Fix Review)

  • Gather context and understand the PR
  • Read current state of changed files
  • Analyze for bugs, security issues, and correctness
  • Post comprehensive review feedback

Good progress — most of the previously flagged issues have been addressed. Here's what was fixed and what still needs attention.


What Was Fixed ✅

Issue Status
run-test.sh: no JSON content validation Fixed — now checks status, findings, and escalation
run-test.sh: unsafe for f in $(find ...) Fixed — uses while IFS= read -r -d '' f
generate-load.sh: missing set -e/pipefail Fixed — now set -euo pipefail
generate-load.sh: 500 background curl processes Fixed — uses xargs -P 20
target/start.sh: silent node_exporter failure Fixed — kill -0 $NODE_EXPORTER_PID liveness check added
setup.sh: overstated drift-detection claim Fixed — comment now accurately scopes what's regenerated
alertmanager.yml: redundant sub-routes Fixed — routes block removed entirely
docker-compose.yml: duplicate --auto-approve-safe Fixed — env var removed, CLI flag kept

Still Needs Attention

chmod 0777 on the investigations directory (run-test.sh:25)

This is still present. Since ./agent-investigations:/app/.agent is a bind-mount that replaces the Dockerfile's pre-created /app/.agent directory, the container user (UID 10001) needs to create /app/.agent/investigations/ inside it. The cleanest fix is to pre-create the subdirectory before starting the stack so the container user only needs to write into an existing directory rather than create subdirectories:

mkdir -p agent-investigations/investigations
chmod 0755 agent-investigations agent-investigations/investigations

Fix this →


Docs component inventory still references hey/wrk2 (docs/e2e-load-investigation-test-plan.md:17,42)

Line 17 (ASCII diagram) and line 42 (component table) still show rakyll/hey or williamyao/wrk2 as the load generator. The actual implementation uses stress-ng + curl. Fix this →


Pass condition accepts failed + escalated investigations (run-test.sh:74-77)

The current check passes the test if an investigation has status: failed as long as escalated is truthy. A failed investigation with an escalation flag is not the same as a successful root cause analysis. If the goal is to verify end-to-end AI investigation quality, consider restricting the pass condition to status: completed:

if data.get('status') not in ('completed', 'escalated'):
    sys.exit(1)
if not data.get('findings') and not data.get('escalated'):
    sys.exit(1)

This is a judgment call — if a high-severity escalation without findings is an acceptable outcome for this test, keep as-is.


Minor Notes

nc server handles only one connection at a time (target/server.sh:3-6)

nc -l in a while true loop accepts connections serially. The 500-request HTTP flood (xargs -P 20) means up to 19 of 20 concurrent requests see "Connection refused" or timeout at any given moment. The || true in generate-load.sh:14 masks these failures. This is fine if the purpose of the HTTP flood is only to add load to trigger Prometheus CPU/load alerts — the stress-ng phase does the actual metric pressure. Worth noting in the test plan that the HTTP flood is load-generating, not latency-testing.

depends_on without health checks (docker-compose.yml:24-74)

Prometheus still starts as soon as Alertmanager's container starts (not when it's ready), and Alertmanager starts as soon as Prometheus starts. For a short-lived E2E test this is unlikely to cause real failures since all services initialize quickly, and the run-test.sh wait loop on /ready provides the real gate. Leaving this as-is is acceptable; just note that flaky startup behavior is possible in heavily loaded CI environments.

repeat_interval: 1m (alertmanager/alertmanager.yml:8)

For a 300s test window, alerts can re-fire up to 4 times. This could trigger duplicate investigation runs. Acceptable for now but worth monitoring if investigation logs show duplicate entries.


Overall this is in good shape. The two substantive fixes remaining are the chmod 0777 (security) and the docs component table (accuracy). The pass-condition question is worth a quick decision.

Comment thread deploy/e2e/run-test.sh

mkdir -p agent-investigations
# Ensure the non-root container user (UID 10001) can write here.
chmod 0777 agent-investigations || true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still 0777. Since the bind-mount replaces /app/.agent, UID 10001 needs to create the investigations/ subdirectory inside it. Pre-creating the subdirectory lets you drop to 0755:

Suggested change
chmod 0777 agent-investigations || true
mkdir -p agent-investigations/investigations
chmod 0755 agent-investigations agent-investigations/investigations

With the investigations/ directory pre-existing, the container user only needs write-into permission, not create-directory permission, so 0755 is sufficient.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-creating the subdirectory doesn't change the underlying permission gap. On a Linux host, writing a new file into a directory requires the write bit on that directory for the writer's UID — not just the execute bit. With 0755, only the owner has write; "others" (the container's UID 10001, which doesn't match the host owner) have read+execute but not write.

Quick proof on the runner this PR's CI uses:

$ mkdir -p /tmp/permtest/investigations && chmod 0755 /tmp/permtest/investigations
$ ls -ld /tmp/permtest/investigations
drwxr-xr-x 2 root root 4096 May 16 15:46 /tmp/permtest/investigations
$ # mode=0o755, others-writable=False — a non-root UID inside the container cannot create files here.

The Dockerfile already pre-creates /app/.agent/investigations chowned to agent:agent (UID 10001), but the bind mount ./agent-investigations:/app/.agent shadows that chowned directory with one owned by whichever UID ran mkdir on the host (root in CI). So unless we either (a) chown 10001:10001 agent-investigations on the host (needs root), or (b) add user: "10001:10001" to the code-agent service in compose to match the host owner, the only thing keeping writes working today is the 0777. Happy to switch to the user: directive approach if you'd prefer a tighter mode — that's a real fix; 0755 alone is not.


Generated by Claude Code

Comment thread deploy/e2e/run-test.sh Outdated
data = json.load(fh)
except (OSError, json.JSONDecodeError):
sys.exit(1)
if data.get('status') not in ('completed', 'failed', 'escalated'):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A failed investigation that also has escalated set will pass the test here. If the intent is to validate that the agent produced a genuine root-cause analysis, consider restricting to completed and escalated only:

Suggested change
if data.get('status') not in ('completed', 'failed', 'escalated'):
if data.get('status') not in ('completed', 'escalated'):

@claude

claude Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Anthony-Bible's task in 1m 54s —— View job


PR Review: E2E Load-Investigation Test Harness (Latest State)

  • Read latest file state after recent commits
  • Check which issues from previous reviews are now resolved
  • Assess Anthony Bible's counter-argument on chmod 0777 vs 0755
  • Check docs for hey/wrk2 vs stress-ng/curl accuracy
  • Review pass condition logic changes (fcfa533)
  • Post updated review feedback

What's Fixed in the Latest Commits ✅

Issue Commit Status
run-test.sh pass condition accepted failed status fcfa533 Fixed — only completed/escalated now pass
generate-load.sh: missing set -euo pipefail 9ea77f7 Fixed
generate-load.sh: 500 background curl processes 9ea77f7 Fixed — now xargs -P 20
target/start.sh: silent node_exporter failure 9ea77f7 Fixed — kill -0 liveness check added
alertmanager.yml: redundant sub-routes previous Fixed — routes block removed entirely
docker-compose.yml: duplicate --auto-approve-safe previous Fixed — env var removed
run-test.sh: unsafe for f in $(find ...) previous Fixed — null-delimited while read

The harness is now significantly more reliable and correct. Two issues remain.


Remaining: chmod 0777 on agent-investigations/

Anthony Bible's counter-argument is technically correct: chmod 0755 alone won't work when the bind-mount directory is owned by the host user (root in CI) and the container runs as UID 10001. 0755 only gives write permission to the owner.

The practical fix is to add a user: directive to the code-agent service so the process runs as the host UID that owns the directory:

# docker-compose.yml, code-agent service
code-agent:
  ...
  user: "${UID:-0}:${GID:-0}"

Then in run-test.sh, before docker compose up -d:

export UID GID
mkdir -p agent-investigations/investigations
chmod 0755 agent-investigations agent-investigations/investigations

With this, the container runs as the same UID that created the directory, so 0755 is sufficient and the Dockerfile's UID 10001 security posture is replaced by a host-UID-matched run. This is the standard Docker bind-mount pattern for CI.

Alternatively, if the security context is less critical and simplicity is preferred, the current 0777 is a known and contained risk (test environment only). If you go that route, a comment explaining why it can't be narrowed would prevent future reviewers from raising this again.

Fix this →


Remaining: Docs component table still references hey/wrk2

docs/e2e-load-investigation-test-plan.md:17 (ASCII diagram) and docs/e2e-load-investigation-test-plan.md:42 (component table) still say hey/wrk2. The actual implementation in load-gen/Dockerfile uses stress-ng + curl. This is a documentation accuracy issue.

Fix this →


Minor Notes (No Action Required)

  • nc server handles one connection at a time (target/server.sh:3-6): The xargs -P 20 flood hits a serial nc -l server, so most concurrent requests timeout. This is intentional — the purpose is CPU/load pressure via stress-ng, not latency testing. The || true in generate-load.sh:14 correctly masks the expected connection failures.
  • depends_on without health checks (docker-compose.yml:24-74): Prometheus and Alertmanager start without readiness guarantees. Acceptable for this harness given the 300s timeout window and the /ready gate on the agent.
  • repeat_interval: 1m (alertmanager/alertmanager.yml:8): Can trigger duplicate investigations in the 300s window. Fine for CI, but worth monitoring if duplicate investigation files cause flaky test behaviour.
  • e2e-target skill hints at expected root cause (agent-config/skills/e2e-target/SKILL.md:41): The skill tells the agent to look for stress-ng. This is good for reliability but means the test validates "agent follows a guide" more than "agent finds root cause autonomously" — worth noting in test interpretation.

Overall this PR is in good shape. The core E2E logic is correct and the previous critical issues have been resolved. Merging is reasonable; the two remaining items (chmod 0777 and docs accuracy) can be addressed in a follow-up if preferred.

@claude

claude Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

PR Review: E2E Load-Investigation Test Harness (Post-Sync Review)

  • Gather context and understand the PR
  • Read new commits and changed files
  • Check status of previously flagged issues
  • Review new changes for bugs and correctness
  • Post comprehensive review feedback

View job run

@Anthony-Bible
Anthony-Bible force-pushed the claude/implement-e2e-plan-yt1oC branch from 75bd97b to 68004ba Compare May 16, 2026 17:17
@claude

claude Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Anthony-Bible's task in 2m 2s —— View job


PR Review: E2E Load-Investigation Test Harness (Post-Sync Review)

  • Gather context and understand the PR
  • Read new commits and changed files
  • Check status of previously flagged issues
  • Review new changes for bugs and correctness
  • Post comprehensive review feedback

This sync brought one important new change — the persistence fix in alert_investigation.go — along with the previously applied shell harness fixes. Here's the complete state.


New Changes in This Sync ✅

alert_investigation.go: Critical persistence bug fixed

The key fix for E2E correctness. Previously store.Update() was called with a status-only stub (newSimpleInvestigationRecord(invID, alert.ID(), "", result.Status)), which wiped all findings, confidence, duration, and escalation data when persisting. The fix builds a full simpleInvestigationRecord from the complete InvestigationResult. The run-test.sh pass condition that checks data.get('findings') would have failed silently before this.

Also improved: the error from store.Update was previously swallowed with _ =; it's now logged properly.

Dockerfile: jq added

Used by run-test.sh:110 for structured debug output in the timeout path. A clean addition.


Previously Fixed Issues ✅

Issue Status
generate-load.sh: missing set -euo pipefail Fixed
generate-load.sh: 500 background curl processes Fixed — xargs -P 20
target/start.sh: silent node_exporter failure Fixed — kill -0 liveness check
run-test.sh: unsafe for f in $(find ...) Fixed — null-delimited while read
run-test.sh: pass condition didn't validate content Fixed — checks status + findings
alertmanager.yml: redundant sub-routes Fixed — removed entirely
docker-compose.yml: duplicate --auto-approve-safe Fixed — env var removed

Still Needs Attention

chmod 0777 on agent-investigations/ (run-test.sh:25)

As discussed in thread, Anthony's counter-argument is correct: 0755 alone doesn't solve the problem when the host directory is owned by root and the container runs as UID 10001. The cleanest fix is the user: directive approach suggested in the previous review — it was never applied:

# docker-compose.yml, code-agent service
code-agent:
  ...
  user: "${UID:-0}:${GID:-0}"

With this, the container runs as the same UID that owns the bind-mount directory, and chmod 0755 is sufficient. If this approach is taken, run-test.sh:23-25 should also pre-create the investigations/ subdirectory:

mkdir -p agent-investigations/investigations
chmod 0755 agent-investigations agent-investigations/investigations

If the security posture is acceptable for a test-only environment and simplicity is preferred, add a comment at run-test.sh:25 explaining why 0777 is required — it will prevent this from being re-raised by future reviewers.

Fix this →


Docs component table still references hey/wrk2 (docs/e2e-load-investigation-test-plan.md:17,42)

The ASCII diagram (line 17) shows (hey/wrk2) and the component table (line 42) reads rakyll/hey or williamyao/wrk2. The actual implementation in load-gen/Dockerfile uses stress-ng + curl. This was flagged in both previous reviews and is still unresolved.

Fix this →


Pre-existing Bug (Not in Scope, Worth Noting)

StopInvestigation can't actually cancel a running investigation (alert_investigation.go:404)

_, cancel := context.WithCancel(ctx)  // derived ctx is discarded

The derived (cancellable) context is never passed to RunInvestigation — both StartInvestigation and RunInvestigation receive the original ctx from the caller. Calling StopInvestigation fires the cancel func, but nothing is listening on the derived context, so runner.Run(ctx, ...) continues uninterrupted. This is pre-existing and not introduced by this PR, but it means the stop API silently does nothing.


Minor Notes

  • The run-test.sh:41 readiness check at localhost:8091/ready is correct — docker-compose.yml:71 maps 8091:8081, and the agent does bind /ready on :8081 separately from the webhook port :8080. No issue here.
  • repeat_interval: 1m in Alertmanager can still trigger duplicate investigations in the 300s window. Acceptable for CI but worth monitoring if investigation logs show duplicate entries.
  • The e2e-target skill (SKILL.md:41) tells the agent to look for stress-ng workload. This makes the test reliable but means it validates "agent follows a guide" more than "agent finds root cause autonomously" — worth noting in test result interpretation.

Overall the harness is in solid shape. The core persistence bug fix is the key correctness change in this sync. Two items remain: the chmod 0777 (security hygiene) and the docs component table (accuracy). Both are reasonable to address as a follow-up or pre-merge depending on the team's bar.

…tence

Adds an end-to-end test rig under deploy/e2e that brings up
node_exporter, Prometheus, Alertmanager, a stress workload, and the
code-agent webhook server, then asserts that a synthetic CPU/memory/load
spike makes it through alerting and produces a populated RCA artifact.
docs/e2e-load-investigation-test-plan.md documents the design.

Also fixes a real bug uncovered while bringing the harness up:
RunInvestigation persisted async completions with a status-only stub,
wiping findings, completed_at, duration, confidence, and escalation
state that the runner had already populated. Builds the update record
from the full InvestigationResult so async investigations end up with
the same persisted detail as synchronous ones, and surfaces the error
instead of discarding it.

Harness specifics:
- HighLoadAverage/HighMemoryUtilization at severity:critical so they
  survive the agent's AutoInvestigateWarning=false filter.
- load-gen uses --cpu 0 (all cores) and 4x1G --vm-keep to actually
  breach CPU/memory thresholds during the 120s stress window.
- AGENT_MODEL pinned to claude-haiku-4-5-20251001 in compose; the
  bundled default is not an Anthropic model.
- jq added to the runtime image so investigations can be inspected
  from inside the container.
- Success criterion is "at least one investigation completed with
  findings"; one good RCA proves the pipeline works even if other
  attempts exhaust the action budget.
- On timeout, dump per-file investigation summaries so failures are
  diagnosable without manually walking the volume.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Anthony-Bible
Anthony-Bible force-pushed the claude/implement-e2e-plan-yt1oC branch from 68004ba to f8ee9c3 Compare May 16, 2026 17:27
@claude

claude Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Anthony-Bible's task in 2m 21s —— View job


PR Review: E2E Load-Investigation Test Harness (Latest State)

  • Gather context from previous reviews
  • Read current state of all changed files
  • Check status of previously flagged issues
  • Review new changes for correctness and quality
  • Post comprehensive review feedback

This is in solid shape. The critical persistence bug fix in alert_investigation.go is the most impactful change and is correct. Most of the shell script issues from previous reviews have been resolved. Two issues remain.


What's Fixed ✅

Issue Status
alert_investigation.go: persistence stub wiped all findings/duration/confidence Fixed — full simpleInvestigationRecord built from InvestigationResult
alert_investigation.go: store.Update error was swallowed with _ = Fixed — now logged
run-test.sh: pass condition didn't validate investigation content Fixed — checks status + findings
run-test.sh: accepted failed + escalated as passing Fixed — only completed/escalated accepted
run-test.sh: unsafe for f in $(find ...) Fixed — null-delimited while read
generate-load.sh: missing set -euo pipefail Fixed
generate-load.sh: 500 background curl processes Fixed — xargs -P 20
target/start.sh: silent node_exporter failure Fixed — kill -0 liveness check
alertmanager.yml: redundant sub-routes Fixed — routes block removed
docker-compose.yml: duplicate --auto-approve-safe Fixed — env var removed
Dockerfile: jq missing for debug output Fixed — jq added to apk

Still Needs Attention

chmod 0777 appears in two places (run-test.sh:25 and setup.sh:14)

The fix agreed upon in thread — adding user: "${UID:-0}:${GID:-0}" to the code-agent service in docker-compose.yml — was never applied. Both files still use 0777. setup.sh:14 now has a comment explaining why (Container runs as UID 10001, so the host dir needs to be world-writable), which is better than nothing, but the run-test.sh:25 comment is less explicit.

If the user: directive approach is taken:

# docker-compose.yml, code-agent service
user: "${UID:-0}:${GID:-0}"
# run-test.sh, replacing lines 23-25
export UID GID
mkdir -p agent-investigations/investigations
chmod 0755 agent-investigations agent-investigations/investigations

If you prefer to keep 0777 for simplicity (acceptable for a test-only harness), at minimum update run-test.sh:25's comment to match setup.sh:14's more informative explanation so future reviewers don't re-raise this.

Fix this →


Docs component table still references hey/wrk2 (docs/e2e-load-investigation-test-plan.md:17,42)

Line 17 (ASCII diagram): (hey/wrk2) → should be (stress-ng + curl)
Line 42 (component table): rakyll/hey or williamyao/wrk2 → should be stress-ng + curl (xargs -P 20)

This was flagged in three consecutive previous reviews and is the only docs accuracy issue remaining.

Fix this →


New Observation: podman compose vs docker compose

run-test.sh uses podman compose throughout (lines 30, 34, 37, 48, 90, 117), while the compose file is named docker-compose.yml. This is intentional if the target environment has Podman, but worth calling out:

  • podman compose rootless mode maps UIDs differently from Docker — this actually interacts with the chmod 0777 issue since rootless Podman may handle the bind-mount UID mapping transparently on some systems
  • If this harness is meant to run on standard Docker CI (GitHub Actions default runners), docker compose should be used instead
  • If Podman is the intentional runtime, the README/docs should mention this requirement

Pre-existing Bug (Not Introduced by This PR)

StopInvestigation cancellation is silently broken (alert_investigation.go:404)

_, cancel := context.WithCancel(ctx)  // derived ctx discarded — cancel fires nothing

The cancellable context is created but never passed to RunInvestigation; both callers receive the original ctx. This is pre-existing, not in scope for this PR, but means the stop API does nothing.


Minor Notes (No Action Required)

  • nc server handles one connection at a time (target/server.sh:3-6). xargs -P 20 flood means up to 19 concurrent requests see connection refused. The || true in generate-load.sh:14 masks this correctly — HTTP load is only for metric pressure, not latency testing.
  • depends_on without health checks (docker-compose.yml:24-74). Acceptable for this harness; the run-test.sh /ready poll is the real gate.
  • repeat_interval: 1m can trigger duplicate investigations in the 300s window. Fine for CI.
  • The e2e-target skill tells the agent to look for stress-ng — test validates "agent follows a guide" more than unsupervised root-cause discovery.

Overall this PR is ready to merge with the two remaining items (chmod 0777 and docs accuracy) either addressed pre-merge or tracked as a follow-up. The core persistence fix and test correctness improvements are solid.

@Anthony-Bible
Anthony-Bible merged commit 7f26407 into main May 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants